package controller

import (
	"encoding/json"
	"errors"
	"fmt"
	"godesk/app/model"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	rt "runtime"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/wailsapp/wails/v2/pkg/runtime"
	"gorm.io/gorm"
)

type Note struct {
	ID        string   `json:"id"`
	Title     string   `json:"title"`
	Content   string   `json:"content"`
	FileName  string   `json:"fileName"`
	NotePath  string   `json:"notePath"`
	Category  category `json:"category"`
	CreatedAt int64    `json:"createdAt"`
	UpdatedAt int64    `json:"updatedAt"`
	DeletedAt int64    `json:"deletedAt"`
}

type category struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type NoteApp struct {
	Base
	DataPath string
	mu       sync.Mutex
}

func NewNote() *NoteApp {
	app := &NoteApp{}
	app.DataPath = filepath.Join(app.getAppPath(), "notes")
	if err := os.MkdirAll(app.DataPath, 0755); err != nil {
		runtime.LogErrorf(app.ctx, "创建目录失败: %v", err)
		return nil
	}

	export := filepath.Join(app.DataPath, "exports")
	if err := os.MkdirAll(export, 0755); err != nil {
		runtime.LogErrorf(app.ctx, "创建目录失败: %v", err)
		return nil
	}
	return app
}

func (n *NoteApp) SaveNote(id, title, content, categoryid string) (Note, error) {
	n.mu.Lock()
	defer n.mu.Unlock()
	filename := fmt.Sprintf("%s.md", title)
	notePath := filepath.Join(n.DataPath, filename)

	modelCategory := model.Category{}
	tx := n.db().Where(&model.Category{ID: categoryid}).First(&modelCategory)
	if tx.Error != nil {
		if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
			runtime.LogDebugf(n.ctx, "查询类别失败: %v", tx.Error)
			modelCategory.ID = "10086"
			modelCategory.Name = "未分类"
		} else {
			return Note{}, tx.Error
		}
	}

	var mNote model.Note
	var note Note
	note.ID = id
	note.Title = title
	note.Content = content
	note.FileName = filename
	note.Category = category{
		ID:   modelCategory.ID,
		Name: modelCategory.Name,
	}
	note.NotePath = notePath
	tx = n.db().Model(&model.Note{}).Where("id = ?", id).First(&mNote)
	if tx.Error != nil {
		if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
			//数据库不存在

			//信息存内存
			note.CreatedAt = time.Now().Unix()
			//将modelCategory转化为string类型
			BMC, err := json.Marshal(modelCategory)
			if err != nil {
				return Note{}, err
			}
			//信息存库
			mNote := model.Note{
				ID:        note.ID,
				Title:     note.Title,
				Content:   note.Content,
				FileName:  note.FileName,
				Category:  string(BMC),
				NotePath:  note.NotePath,
				CreatedAt: note.CreatedAt,
				UpdatedAt: note.UpdatedAt,
			}
			if err := n.db().Create(&mNote).Error; err != nil {
				runtime.LogErrorf(n.ctx, "保存笔记失败: %v", err)
				return Note{}, err
			}

			err = os.WriteFile(notePath, []byte(content), 0644)
			if err != nil {
				return Note{}, err
			}
			return note, nil
		} else {
			return Note{}, tx.Error
		}
	}

	//已存在则更新时间
	note.CreatedAt = mNote.CreatedAt
	note.UpdatedAt = time.Now().Unix()

	//更新数据库字段
	mNote.Title = note.Title
	mNote.Content = note.Content
	mNote.FileName = note.FileName
	mNote.NotePath = note.NotePath
	mNote.UpdatedAt = note.UpdatedAt
	if err := n.db().Save(&mNote).Error; err != nil {
		runtime.LogErrorf(n.ctx, "更新笔记失败: %v", err)
		return Note{}, err
	}

	//写入文件，当文件不存在时创建，存在时覆盖
	err := os.WriteFile(notePath, []byte(content), 0644)
	if err != nil {
		return Note{}, err
	}
	return note, nil
}

func (n *NoteApp) DeleteNote(id string) error {
	n.mu.Lock()
	defer n.mu.Unlock()

	var note model.Note
	tx := n.db().Model(&model.Note{}).Where("id = ?", id).First(&note)
	if tx.Error != nil {
		return tx.Error
	}

	err := os.Remove(note.NotePath)
	if err != nil {
		return err
	}

	tx = n.db().Delete(&note)
	if tx.Error != nil {
		return tx.Error
	}

	return nil
}

// ExportNote 导出笔记
func (n *NoteApp) ExportNote(noteID string, format string) (bool, error) {
	if noteID == "" {
		return false, errors.New("note ID cannot be empty")
	}

	var mNote model.Note
	tx := n.db().Model(&model.Note{}).Where("id = ?", noteID).First(&mNote)
	// 示例实现:
	if tx.Error != nil {
		return false, tx.Error
	}

	var exportPath string
	var err error

	switch format {
	case "markdown":
		exportPath = filepath.Join(n.DataPath, "exports", mNote.FileName)
		runtime.LogDebugf(n.ctx, "源地址: %s 目标地址：%s", mNote.NotePath, exportPath)
		err := copyFile(mNote.NotePath, exportPath)
		if err != nil {
			runtime.LogErrorf(n.ctx, "赋值文件失败: %v", err)
			return false, err
		}
	case "html":
		runtime.LogErrorf(n.ctx, "功能未实现")
		return false, errors.New("功能未实现")
	default:
		return false, errors.New("unsupported export format")
	}

	if err != nil {
		return false, err
	}

	return true, nil
}

func copyFile(source, destination string) error {
	// 打开源文件
	srcFile, err := os.Open(source)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	// 创建目标文件
	dstFile, err := os.Create(destination)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	// 复制文件内容
	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return err
	}

	return nil
}

func (n *NoteApp) LoadNote(id string) (string, error) {
	n.mu.Lock()
	defer n.mu.Unlock()
	runtime.LogDebugf(n.ctx, "加载笔记id: %v", id)

	var node model.Note
	tx := n.db().Where(&model.Note{ID: id}).First(&node)
	if tx.Error != nil {
		return "", tx.Error
	}

	return node.Content, nil
}

func (n *NoteApp) GetNoteList() ([]Note, error) {
	n.mu.Lock()
	defer n.mu.Unlock()
	nlist := make([]Note, 0)
	var notelist []model.Note
	tx := n.db().Find(&notelist)
	if tx.Error != nil {
		runtime.LogErrorf(n.ctx, "查询笔记失败: %v", tx.Error)
		return nlist, tx.Error
	}

	for _, m := range notelist {
		var cate model.Category
		err := json.Unmarshal([]byte(m.Category), &cate)
		if err != nil {
			runtime.LogDebugf(n.ctx, "解析类别失败: %v", err)
			continue
		}

		nlist = append(nlist, Note{
			ID:       m.ID,
			Title:    m.Title,
			Content:  m.Content,
			FileName: m.FileName,
			Category: category{
				ID:   cate.ID,
				Name: cate.Name,
			},
			NotePath:  m.NotePath,
			CreatedAt: m.CreatedAt,
			UpdatedAt: m.UpdatedAt,
			DeletedAt: m.DeletedAt,
		})
	}
	return nlist, nil
}

func (n *NoteApp) MoveNote(noteID string, targetCategoryID string) (bool, error) {
	if noteID == "" || targetCategoryID == "" {
		return false, errors.New("note ID and target category ID cannot be empty")
	}
	// 1. 查询笔记
	var note model.Note
	tx := n.db().Model(&model.Note{}).Where("id = ?", noteID).First(&note)
	if tx.Error != nil {
		return false, tx.Error
	}

	var cate model.Category
	tx = n.db().Model(&model.Category{}).Where("id = ?", targetCategoryID).First(&cate)
	if tx.Error != nil {
		return false, tx.Error
	}

	// 2. 更新笔记的类别
	Category, err := json.Marshal(cate)
	if err != nil {
		return false, err
	}
	note.Category = string(Category)
	note.UpdatedAt = time.Now().Unix()
	// 3. 保存更新后的笔记
	tx = n.db().Save(&note)
	if tx.Error != nil {
		return false, tx.Error
	}
	return true, nil
}

func (n *NoteApp) GetCategories() ([]model.Category, error) {
	n.mu.Lock()
	defer n.mu.Unlock()
	list := make([]model.Category, 0)
	tx := n.db().Model(&model.Category{}).Find(&list)
	if tx.Error != nil {
		return list, tx.Error
	}
	return list, nil
}

func (n *NoteApp) AddCategory(name string) (string, error) {
	n.mu.Lock()
	defer n.mu.Unlock()

	category := &model.Category{
		ID:   uuid.NewString(),
		Name: name,
	}

	//类别存入数据库
	if err := n.db().Create(&category).Error; err != nil {
		runtime.LogErrorf(n.ctx, "创建类别失败: %v", err)
		return "", err
	}

	return category.ID, nil
}

func (n *NoteApp) DeleteCategory(id string) error {
	n.mu.Lock()
	defer n.mu.Unlock()
	tx := n.db().Delete(&model.Category{}, "id = ?", id)
	if tx.Error != nil {
		return tx.Error
	}
	return nil
}

func (n *NoteApp) UpdateCategory(id, name string) error {
	n.mu.Lock()
	defer n.mu.Unlock()
	tx := n.db().Model(&model.Category{}).Where("id = ?", id).Update("name", name)
	if tx.Error != nil {
		return tx.Error
	}
	return nil
}

func (n *NoteApp) OpenExportDirectory() error {
	exportDir := filepath.Join(n.DataPath, "exports")
	if _, err := os.Stat(exportDir); os.IsNotExist(err) {
		if err := os.Mkdir(exportDir, 0755); err != nil {
			return err
		}
	}

	var cmd string
	var args []string

	switch rt.GOOS {
	case "windows":
		cmd = "explorer"
		args = []string{exportDir}
	case "darwin":
		cmd = "open"
		args = []string{exportDir}
	case "linux":
		cmd = "xdg-open"
		args = []string{exportDir}
	default:
		return errors.New("unsupported platform")
	}

	return exec.Command(cmd, args...).Start()
}
